// * 错误处理 try...catch

try {
	// 代码 ...
} catch (err) {
	// 错误捕获（try 中的代码运行时发现错误才会执行这里的代码）
}

// * 执行步骤:
// * 1. 首先，执行 try { ... } 中的代码。
// * 2. 如果这里没有错误，则忽略 catch(err)：执行到 try 的末尾并跳过 catch 继续执行。
// * 3. 如果这里出现错误，则 try 执行停止，控制流转向 catch(err) 的开头。变量 err （我们可以使用任何名称）将包含一个 error 对象，该对象包含了所发生事件的详细信息。

// *********************************************************************************************************************************************************

// * try...catch 同步执行（try 中包含的代码必须是同步代码，异步代码中的错误不会被捕获）

// try {
// 	setTimeout(() => {
// 		noSuchVariable // 未定义的变量
// 	})
// } catch (error) {
// 	console.log('不工作')
// }

// 解决方法：
// setTimeout(() => {
// try {
// 	noSuchVariable // 未定义的变量
// } catch (error) {
// 	console.log('工作')
// }
// })

// *********************************************************************************************************************************************************

// * Erro 对象，发生错误时 JS 会生成一个包含有关此 error 详细信息的对象。然后将该对象作为参数传递给 catch(err)。

// * 内建错误对象，具有两个主要属性：
// * 1. name：Error名称，例如，对于一个未定义的变量，名称是 "ReferenceError"。
// * 2. message：关于 error 的详细文字描述。
// * 3. stack：当前调用栈。用于调试目的的一个字符串，其中包含有关导致 error 的嵌套调用序列的信息。

// 例子：
try {
	lalala
} catch (error) {
	console.log('错误名称：', error.name) // 错误名称： ReferenceError
	console.log('错误信息：', error.message) // 错误信息： lalala is not defined

	console.log('错误调用栈：', error.stack)
	// 错误调用栈： ReferenceError: lalala is not defined
	//   at Object.<anonymous> (d:\project\2024-web-study\07-现代JavaScript教程\【1】、第一部分(JavaScript编程语言)\10-错误处理\001-错误处理，try...catch\index.js:46:2)
	//   at Module._compile (node:internal/modules/cjs/loader:1358:14)
	//   at Module._extensions..js (node:internal/modules/cjs/loader:1416:10)
	//   at Module.load (node:internal/modules/cjs/loader:1208:32)
	//   at Module._load (node:internal/modules/cjs/loader:1024:12)
	//   at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:174:12)
	//   at node:internal/main/run_main_module:28:49

	console.log('整体错误信息：', String(error)) // 整体错误信息： ReferenceError: lalala is not defined
}

// *********************************************************************************************************************************************************

// * 不需要详细的错误信息，catch 也可以省略(error)。

try {
	lll
} catch {
	console.log('出错啦~') // 出错啦~
}

// *********************************************************************************************************************************************************

// * "throw" 操作符，立即抛出一个错误。语法：throw <error object>
// * 技术上来讲，我们可以将任何东西用作 error 对象。甚至可以是一个原始数据类型，例如数字或字符串，但最好使用对象（最好是具有 name 和 message 属性的对象，保持与内建 error 的兼容性）。
// * JS 中内建的标准 error 的构造器： Error, SyntaxError, ReferenceError, TypeError 等，也可以使用这些来创建 error 对象。

// 内建错误对象例如：
let err1 = new Error('错误信息')
console.log('构造函数自身的name属性：', err1.name) // 构造函数自身的name属性： Error
console.log('message的值来自于，传入构造函数的参数', err1.message) // message的值来自于，传入构造函数的参数 错误信息

let err2 = new SyntaxError('错误信息')
let err3 = new TypeError('错误信息')

// 自定义错误例子：
let json = '{ "age": 30 }'

try {
	let user = JSON.parse(json)

	if (!user.name) {
		// 立即抛出错误，代码停止继续往下执行
		throw new SyntaxError('数据不全：没有 name')
	}

	console.log(user.name)
} catch (error) {
	console.log('JSON Error: ' + error) // JSON Error: SyntaxError: 数据不全：没有 name
}

// *********************************************************************************************************************************************************

// * 捕获，再次抛出（Rethrowing）的错误
function readDate() {
	let json = '{ "age": 30 }'

	try {
		blabla() // error
	} catch (error) {
		if (!(error instanceof SyntaxError)) {
			throw error // 再次抛出错误
		}
	}
}

// 这里可以再次捕获 readDate 函数中 try...catch 抛出的错误
try {
	readDate()
} catch (error) {
	console.log('readDate 函数再次抛出的错误: ' + error) // readDate 函数再次抛出的错误: ReferenceError: blabla is not defined
}

// *********************************************************************************************************************************************************

// * try...catch...finally

// * finally 在所有情况下都会执行:
try {
	// ... 尝试执行的代码 ...
} catch (error) {
	// ... 处理 error ...
} finally {
	// ... 总是会执行的代码 ...
}

// * 例子1：finally 和 return
function func() {
	try {
		return 1 // 即便这里 return 返回了，finally 依然会执行
	} catch (error) {
	} finally {
		console.log('finally')
	}
}

console.log(func()) // finally 1。 先执行 finally 中的打印，再执行外部的打印。

// * 例子2：try 中抛出错误 finally 也会执行

// 会先打印 finally，后抛出错误
try {
	throw new Error('22222222222222 - 错误信息')
} finally {
	console.log('1111111111111 - finally finally finally finally finally finally finally')
}

// *********************************************************************************************************************************************************

// * 全局 catch：在浏览器中，我们可以将一个函数赋值给特殊的 window.onerror 属性，该函数将在发生未捕获的 error 时执行。

// window.onerror = function (msg, url, lineNo, columnNo, error) {
// 	console.log('全局错误信息：', error) // 全局错误信息： ReferenceError: lalala is not defined
// }

// * 全局错误信息参数：
// message
// error 信息。

// url
// 发生 error 的脚本的 URL。

// line，col
// 发生 error 处的代码的行号和列号。

// error
// error 对象。

// * 例子：
// window.onerror = function (message, url, line, col, error) {
// 	alert(`${message}\n At ${line}:${col} of ${url}`)
// }

// function readData() {
// 	badFunc() // 啊，出问题了！
// }

// readData()

// * 全局错误处理程序 window.onerror 的作用通常不是恢复脚本的执行 —— 如果发生编程错误，恢复脚本的执行几乎是不可能的，它的作用是将错误信息发送给开发者。

// *********************************************************************************************************************************************************
